﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WT.Sockert.Interfaces;

namespace WT.Sockert {
    public class SocketWrapper : ISocket {

        public readonly Socket _socket;
        private Stream _stream;
        private CancellationTokenSource _tokenSource;
        private TaskFactory _taskFactory;

        public SocketWrapper(Socket socket) {
            _tokenSource = new CancellationTokenSource();
            _taskFactory = new TaskFactory(_tokenSource.Token);
            _socket = socket;
            if (_socket.Connected)
                _stream = new NetworkStream(_socket);
        }

        public bool
            Connected {
            get {
                return _socket.Connected;
            }
        }

        public EndPoint LocalEndPoint {
            get {
               
                return _socket.LocalEndPoint;
            }
        }
        public EndPoint RemoteEndPoint {
             get {
                return _socket.RemoteEndPoint;
            }
        }

        public bool NoDelay {
            get { return _socket.NoDelay; }
            set { _socket.NoDelay = value; }
        }

        public string RemoteIpAddress {
            get {
                var endpoint = _socket.RemoteEndPoint as IPEndPoint;
                return endpoint != null ? endpoint.Address.ToString() : null;
            }
        }

        public int RemotePort {
            get {
                var endpoint = _socket.RemoteEndPoint as IPEndPoint;
                return endpoint != null ? endpoint.Port : -1;
            }
        }

        public Stream Stream {
            get {
                return _stream;
            }
        }

       

        public void Bind(EndPoint ipLocal) {
            _socket.Bind(ipLocal);
        }

        public void Close() {
            _tokenSource.Cancel();
            if (_stream != null) _stream.Close();
            if (_socket != null) _socket.Close();
        }

        public void Dispose() {
            _tokenSource.Cancel();
            if (_stream != null) _stream.Dispose();
            if (_socket != null) _socket.Dispose();
        }

        public void Listen(int backlog) {
            _socket.Listen(backlog);
        }
        /// <summary>
        /// 实例客户socket
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        public Task<ISocket> Accept(Action<ISocket> callback, Action<Exception> error) {
            Func<IAsyncResult, ISocket> end = socket => {
                return _tokenSource.Token.IsCancellationRequested ? null
                 : new SocketWrapper(_socket.EndAccept(socket));
            };
            var task = _taskFactory.FromAsync(_socket.BeginAccept, end, null);
            task.ContinueWith((t) => callback(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
                .ContinueWith((t) => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
            task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
            return task;

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="callback"></param>
        /// <param name="error"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public Task<int> Receive(byte[] buffer, Action<int> callback, Action<Exception> error, int offset = 0) {
            try {
                Func<AsyncCallback, object, IAsyncResult> begin =
               (cb, s) => _stream.BeginRead(buffer, offset, buffer.Length, cb, s);

                Task<int> task = Task.Factory.FromAsync<int>(begin, _stream.EndRead, null);
                task.ContinueWith(t => callback(t.Result), TaskContinuationOptions.NotOnFaulted)
                    .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
                task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
                return task;
            } catch (Exception e) {
                error(e);
                return null;
            }
        }

        public Task Send(byte[] buffer, Action callback, Action<Exception> error) {
            if (_tokenSource.IsCancellationRequested)
                return null;

            try {
                Func<AsyncCallback, object, IAsyncResult> begin =
                    (cb, s) => _stream.BeginWrite(buffer, 0, buffer.Length, cb, s);

                Task task = Task.Factory.FromAsync(begin, _stream.EndWrite, null);
                task.ContinueWith(t => callback(), TaskContinuationOptions.NotOnFaulted)
                    .ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
                task.ContinueWith(t => error(t.Exception), TaskContinuationOptions.OnlyOnFaulted);

                return task;
            } catch (Exception e) {
                error(e);
                return null;
            }
        }
    }
}
